package folder.main.exp2.algorithms;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;

import org.apache.commons.math3.stat.descriptive.SummaryStatistics;

import folder.model.link.Link;
import folder.model.network.Network;
import folder.model.node.Node;

public class Evaluation
{
	private int numberNodesController;
	public int getNumberNodesControllers(){ return this.numberNodesController; }
	
	private double delayMeanController;
	public double getDelayMeanController(){ return this.delayMeanController; }
	
	private double delayMeanNormal;
	public double getDelayMeanNormal(){ return this.delayMeanNormal; }
	
	private double differenceDelays;
	public double getDifferenceDelays(){ return this.differenceDelays; }
	
	private double delayLongerMeanController;
	public double getDelayLongerMeanController(){ return this.delayLongerMeanController; }
	
	private double delayLongerMeanNormal;
	public double getDelayLongerMeanNormal(){ return this.delayLongerMeanNormal; }
	
	private double differenceDelaysLonger;
	public double getDifferenceDelaysLonger(){ return this.differenceDelaysLonger; }
	
	private double occupationPercentage;
	public double getOccupationPercentage(){ return this.occupationPercentage; }
	
	private Network network;
	private LinkedHashMap< Node, Node > matchesNodes;

	private List< Node > nodesController;
	private List< Node > nodesNormal;
		
	public static Evaluation evaluation( Network network, LinkedHashMap< Node, Node > matchesNodes )
	{	
		return new Evaluation( network, matchesNodes );
	}
	
	public Evaluation( Network network, LinkedHashMap< Node, Node > matchesNodes )
	{
		this.network = network;
		this.matchesNodes = matchesNodes;
		
		this.setNodes();
	}
	
	private void setNodes()
	{
		this.setNodesCategory();
		this.setNodesController();
		this.setNodesNormal();
		this.setNumberNodesController();
	}
	
	private void setNodesCategory() 
	{
		Iterator< Entry< Node, Node > > iterator = this.matchesNodes.entrySet().iterator();
		
		while( iterator.hasNext() )
		{
			Entry< Node, Node > entry = iterator.next();
			Node nodeNormal = entry.getKey();
			Node nodeController = entry.getValue();
			
			nodeNormal.setSelected( false );
			nodeNormal.setNodeController( nodeController );
			
			nodeController.setSelected( true );
			nodeController.addNodeControlled( nodeNormal );
		}
	}
	
	private void setNodesController()
	{
		this.nodesController = new ArrayList< Node >();
		
		for( Node node: this.network.getNodes() )
			if( node.isSelected() )
				this.nodesController.add( node );
	}
	
	private void setNodesNormal()
	{
		this.nodesNormal = new ArrayList< Node >();
		
		for( Node node: this.network.getNodes() )
			if( !node.isSelected() )
				this.nodesNormal.add( node );
	}
	
	private void setNumberNodesController()
	{
		this.numberNodesController = this.nodesController.size();
	}
	
	public void run()
	{
		this.setPathsNodesController();
		this.setDelayMeanController();
		this.setDelayLongerMeanController();
		
		this.setPathsNodesNormal();
		this.setDelayMeanNormal();
		this.setDelayLongerMeanNormal();
		
		this.setDifferenceDelays();
		this.setDifferenceDelaysLonger();
		this.setPercentageOccupation();
	}
	
	private void setPathsNodesController()
	{
		this.network.setPathsNodes( this.nodesController );
	}
	
	private void setDelayLongerMeanController()
    {	
		double delayBoundary = 0;
		
		for( Node nodeController: this.nodesController )
			for( Node nodeControlled: nodeController.getNodesControlled() )
			{
				double delay = nodeController.getDelayToNodeTarget( nodeControlled );
				
				if( delay > delayBoundary  )
					delayBoundary = delay;
			}
		
		this.delayLongerMeanController = delayBoundary;
    }
	
	private void setDelayMeanController()
    {
		SummaryStatistics sumTotal = new SummaryStatistics();
		
		for( Node nodeController: this.nodesController )
		{
			SummaryStatistics sum = new SummaryStatistics();
			
			for( Node nodeControlled: nodeController.getNodesControlled() )
			{
				double delay = nodeController.getDelayToNodeTarget( nodeControlled );
				sum.addValue( delay );
			}
			
			sumTotal.addValue( sum.getMean() );
		}
		
		delayMeanController = sumTotal.getMean();
    }
		
	private void setPathsNodesNormal()
	{
		for( Node node: this.nodesController )
			node.disconnectLinks();
		
		this.network.setPathsNodes( this.nodesNormal );

		for( Node node: this.nodesController )
			node.connectLinks();
	}
	
	private void setDelayLongerMeanNormal()
    {
		double delayBoundary = 0;
		
		for( Node nodeSource: this.nodesNormal )
			for( Node nodeTarget: this.nodesNormal )
				if( !nodeTarget.equals( nodeSource ) )
				{
					double delayToNode = nodeSource.getDelayToNodeTarget( nodeTarget );
					
					if( delayToNode > delayBoundary )
						delayBoundary = delayToNode;
				}
		
		this.delayLongerMeanNormal = delayBoundary;
    }
	
	private void setDelayMeanNormal()
    {
		SummaryStatistics sumTotal = new SummaryStatistics();
		
		for( Node nodeSource: this.nodesNormal )
		{
			SummaryStatistics sum = new SummaryStatistics();
			
			for( Node nodeTarget: this.nodesNormal )
				if( !nodeTarget.equals( nodeSource ) )
				{
					double delayToNode = nodeSource.getDelayToNodeTarget( nodeTarget );
					sum.addValue( delayToNode );
				}
			
			sumTotal.addValue( sum.getMean() );
		}
		
		this.delayMeanNormal = sumTotal.getMean();
    }
	
	private void setDifferenceDelays()
	{
		this.differenceDelays = Math.abs( this.delayMeanController - this.delayMeanNormal );
	}
	
	private void setDifferenceDelaysLonger()
	{
		this.differenceDelaysLonger = Math.abs( this.delayLongerMeanController - this.delayLongerMeanNormal );
	}
	
	private void setPercentageOccupation()
	{
		double value = 0;
		
		for( Node node: this.network.getNodes() )
		{
			node.setBandwidthOccupation();
			
			if( node.getBandwidthOccupation() > value )
				value = node.getBandwidthOccupation();
		}
		
		this.occupationPercentage = value;
	}
	
	/*private void setPercentageOccupation()
	{
		SummaryStatistics sum = new SummaryStatistics();
		
		for( Node node: this.network.getNodes() )
		{
			node.setBandwidthOccupation();
			sum.addValue( node.getBandwidthOccupation() );
		}
		
		this.percentageOccupation = sum.getMean();
	}*/
	
	/*private void setPercentageOccupation()
	{
		this.setBandwidthOccupation();
		this.setBandwidthNetwork();
		
		this.percentageOccupation = this.bandwidthOccupation / this.bandwidthNetwork;
	}
	
	private void setBandwidthOccupation()
	{
		SummaryStatistics sum = new SummaryStatistics();
		
		for( Link link: network.getLinks() )
			sum.addValue( link.getBandwidthTotal() - link.getBandwidthAvailable() );
		
		this.bandwidthOccupation = sum.getSum();
	}
	
	private void setBandwidthNetwork()
	{
		SummaryStatistics sum = new SummaryStatistics();
		
		for( Link link: network.getLinks() )
			sum.addValue( link.getBandwidthTotal() );
		
		this.bandwidthNetwork = sum.getSum();
	}*/
	
}
